package main

import (
  "net"
  "fmt"
  "sync"
  "io"
  "time"
)
type Server struct {
  Ip string
  Port int

  // online user lists
  OnlineMap map[string] *User
  mapLock sync.RWMutex

  //broadcast channel
  Message chan string
}

//create a server interface
func NewServer(ip string, port int) *Server{
  server := &Server{
    Ip: ip,
    Port: port,
    OnlineMap: make(map[string]*User),
    Message: make(chan string),
  }
  return server
}

//broadcast
func (this *Server) Broadcast(user *User, msg string){
  sendMsg := "[" + user.Addr + "]" + user.Name + ":" + msg

  this.Message <- sendMsg
}


//listen message and broadcast to all online users
func (this *Server) ListenMessager(){
  for {
    msg := <-this.Message

    // send msg to all user online
    this.mapLock.Lock()
    for _,cli := range this.OnlineMap {
      cli.C <- msg
    }
    this.mapLock.Unlock()
  }
}

func (this *Server) Handle(conn net.Conn){
  user := NewUser(conn, this)

  //...  user online
  fmt.Println("socket connected. user: ",user)

  user.Online()

  isLive := make(chan bool)
  // recive user messages
  go func(){
    buf := make([]byte, 4096)
    for {
      n,err := conn.Read(buf)
      if n == 0 {
        user.Offline()
        return
      }

      if err != nil && err != io.EOF {
        fmt.Println("Conn read err:",err)
        return
      }

      // get message ,stip '\n'
      msg := string(buf[:n-1])

      user.DoMessage(msg)
      //
      isLive <- true
    }
  }()
  //current handle ...
  for {
    select {
    case <-isLive:
    case <-time.After(time.Second * 60):
      user.SendMsg("you has logout because long time no response.") //close, 
      close(user.C)
      conn.Close()
      return //runtime.Goexit()
    }
  }
}

//launch a server
func(this *Server) Start(){
  // socket listen
  listenner,err := net.Listen("tcp", fmt.Sprintf("%s:%d",this.Ip,this.Port))
  if err != nil {
    fmt.Println("net.Listen err: ",err)
    return
  }
  // close listen socket
  defer listenner.Close()

  // listen message and send
  go this.ListenMessager()
  for {
    // accept
    conn, err := listenner.Accept()
    if err != nil {
      fmt.Println("net.Accept err:", err)
      continue
    }
    // do handler
    go this.Handle(conn)
  }
}
